/* eslint-disable react/jsx-props-no-spreading */
import {
  createContext, useContext, useMemo, useState,
} from 'react';
import { IPropChild } from './types';

// context的生成器
interface IStore<T> {
  key:string;
  store:T;
  setStore:(payload:Partial<T>) => void;
}

const cxtCache:Record<string, Cxt> = {};

function getCxtProvider<T>(
  key:string,
  defaultValue:T,
  AppContext:React.Context<IStore<T>>,
) {
  return ({ children }:IPropChild) => {
    const [store, setStore] = useState(defaultValue);
    const value = useMemo(() => ({
      key,
      store,
      setStore: (payload = {}) => setStore((state) => ({
        ...state,
        ...payload,
      })),
    }), [store]);
    return (
      <AppContext.Provider value={value}>
        { children }
      </AppContext.Provider>
    );
  };
}

// 这个地方T=any是为了防止上边 const cxtCache:Record<string, Cxt> = {}; 这个常量报错，因为常量不能加泛型
class Cxt<T = any> {
  defaultStore:IStore<T>;

  AppContext:React.Context<IStore<T>>;

  Provider:({ children }:IPropChild) => JSX.Element;

  constructor(key:string, defaultValue:T) {
    this.defaultStore = {
      key,
      store: defaultValue,
      setStore: () => {},
    };
    this.AppContext = createContext(this.defaultStore); // 创建一个context上下文
    // @ts-ignore
    this.Provider = getCxtProvider(key, this.defaultStore, this.AppContext);
    cxtCache[key] = this;
  }
}

export function useAppContext<T>(key:string) {
  const cxt = cxtCache[key] as Cxt<T>;
  const app = useContext(cxt.AppContext);
  return {
    store: app.store,
    setStore: app.setStore,
  };
}

export function connectFactory<T>(key:string, defaultValue:T) {
  const cxt = cxtCache[key];
  let CurCxt:Cxt<T>;
  if (cxt) {
    CurCxt = cxt;
  } else {
    CurCxt = new Cxt<T>(key, defaultValue);
  }
  return (Child:React.FunctionComponent<any>) => (props:any) => (
    <CurCxt.Provider>
      <Child {...props} />
    </CurCxt.Provider>
  );
}
